package pers.duorhs.csr.service;

import cn.hutool.core.util.StrUtil;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import io.socket.client.Socket;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import pers.duorhs.common.redis.RedisConstance;
import pers.duorhs.common.socket.SocketClientHandler;
import pers.duorhs.common.socket.SocketConstance;
import pers.duorhs.common.socket.SocketData;
import pers.duorhs.common.socket.SocketServerHandler;
import pers.duorhs.common.socket.service.AppSocketService;
import pers.duorhs.common.socket.service.CsrSocketService;
import pers.duorhs.common.util.RedisUtil;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author Duorhs
 * @date 2022/5/9 9:32
 */
@Service
@DubboService
@AllArgsConstructor
@Slf4j
public class CsrSocketServiceImpl implements CsrSocketService {
    private final RedisUtil redisUtil;
    private final SocketServerHandler serverHandler;
    private final SocketClientHandler clientHandler;
    private final SocketIOServer socketIOServer;
    @DubboReference
    private AppSocketService appSocketService;

    @Override
    public void employeeLogin(String employee) {
        clientHandler.login(employee, SocketConstance.CSR_ONLINE);
        redisUtil.hPut(RedisConstance.WORKING_RELATION, employee, "");
        checkQueue();
    }

    @Override
    public void employeeWork(String employee, String customer) {
        redisUtil.hPut(RedisConstance.WORKING_RELATION, employee, customer);
    }

    /**
     * 解除工作关系
     *
     * @param customer
     */
    @Override
    public void workDone(String customer) {
        String employee = findEmployeeByCustomer(customer);
        redisUtil.hPut(RedisConstance.WORKING_RELATION, employee, "");
        checkQueue();
    }

    /**
     * 坐席登出
     *
     * @param employee
     */
    @Override
    public void employeeLogout(String employee) {
        socketIOServer.getRoomOperations(SocketConstance.CSR_ONLINE).sendEvent(Socket.EVENT_DISCONNECT, employee);
    }

    @Override
    public void pushToCsrClient(SocketData data) {
        Object customer = data.getHead().get("customer");
        String employeeByCustomer = findEmployeeByCustomer((String) customer);
        data.getHead().put("employee", employeeByCustomer);
        socketIOServer.getRoomOperations(SocketConstance.CSR_ONLINE).sendEvent(SocketConstance.EVENT, data);
    }


    @Override
    public boolean myEmployeeClient(String room, String name) {
        ArrayList<SocketIOClient> clientsFromRoom = serverHandler.getClientsFromRoom(room);
        return clientsFromRoom.stream().anyMatch(client -> serverHandler.getUser(client).equals(name));
    }

    /**
     * 有新用户开始排队
     */
    @Override
    public void checkQueue() {
        Cursor<Map.Entry<Object, Object>> cursor = redisUtil.hScan(RedisConstance.WORKING_RELATION, ScanOptions.NONE);
        while (cursor.hasNext()) {
            Map.Entry<Object, Object> entry = cursor.next();
            if (StrUtil.isEmptyIfStr(entry.getValue())) {
                if (redisUtil.hasKey(RedisConstance.QUEUE)) {
                    ScanOptions scanOptions = ScanOptions.scanOptions().count(1).build();
                    ZSetOperations.TypedTuple<String> first = redisUtil.zScan(RedisConstance.QUEUE, scanOptions).next();
                    String customer = first.getValue();
                    employeeWork((String) entry.getKey(), customer);
                    redisUtil.zRemove(RedisConstance.QUEUE, customer);
                    log.info("坐席{} 为{} 开始服务", entry.getKey(), customer);
                }
            }
        }
    }

    /**
     * 根据客户找到关联的坐席
     *
     * @param customer
     * @return
     */
    @Override
    public String findEmployeeByCustomer(String customer) {
        Cursor<Map.Entry<Object, Object>> cursor = redisUtil.hScan(RedisConstance.WORKING_RELATION, ScanOptions.NONE);
        while (cursor.hasNext()) {
            Map.Entry<Object, Object> entry = cursor.next();
            if (entry.getValue().equals(customer)) {
                return (String) entry.getKey();
            }
        }
        log.info("找不到 {} 对应的坐席", customer);
        return null;
    }

    /**
     * 坐席给APP推信息(模拟前端的socket发请求)
     *
     * @param employee
     * @param msg
     */
    @Override
    public void push(String employee, String msg) {
        SocketData<String> socketData = new SocketData<>();
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        map.put("employee", employee);
        socketData.setHead(map);
        socketData.setBody(msg);
        appSocketService.pushToAppClient(socketData);
    }
}
